export interface EmitEventMap {}

export type EmitEventNamespace = string | number | object | Symbol

export type EmitEventListenr<T = unknown> = (this: void, ev: T) => any

type CisternListenr = {
  once?: boolean
  listener: EmitEventListenr<any>
}

const eventCisternMap = new Map<EmitEventNamespace, Map<PropertyKey, CisternListenr[]>>()

export function useEmit<T extends EmitEventMap>(namespace: EmitEventNamespace = 'ns') {
  let cistern = eventCisternMap.get(namespace)!
  if (!cistern) eventCisternMap.set(namespace, (cistern = new Map()))

  function on<K extends keyof T>(type: K, listener: EmitEventListenr<T[K]>, once?: boolean) {
    let listenerCistern = cistern.get(type)
    if (!listenerCistern) cistern.set(type, (listenerCistern = []))

    listenerCistern.push({ listener, once })
  }

  function once<K extends keyof T>(type: K, listener: EmitEventListenr<T[K]>) {
    on(type, listener, true)
  }

  function off<K extends keyof T>(type: K, listener: EmitEventListenr<T[K]>) {
    const listenerCistern = cistern.get(type)
    if (listenerCistern) {
      for (let i = listenerCistern.length; i--; ) {
        if (listenerCistern[i].listener === listener) {
          listenerCistern.splice(i, 1)
        }
      }
    }
  }

  function emit<K extends keyof T>(type: K, event: T[K]) {
    const listenerCistern = cistern.get(type)
    if (listenerCistern) {
      for (let i = 0; i < listenerCistern.length; ) {
        const { once, listener } = listenerCistern[i]
        listener(event)

        if (once) {
          listenerCistern.splice(i, 1)
        } else {
          i++
        }
      }
    }
  }

  return { on, once, off, emit }
}
